package com.ktwlrj.core.handler;

import com.ktwlrj.core.base.ActionResult;
import com.ktwlrj.core.base.SuperController;
import com.ktwlrj.core.constant.ResultCode;
import com.ktwlrj.core.exception.ActionException;
import com.ktwlrj.core.util.ThrowableUtil;
import com.ktwlrj.core.util.cache.InMemoryCache;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.nio.file.AccessDeniedException;
import java.util.List;
import java.util.Set;

/**
 * 全局统一异常处理
 *
 * @author zhouxuanhong
 * @date 2019-04-13 12:42
 * @since JDK1.8
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends SuperController {
    private static final String TOKEN_EXPIRED = "Token is expired";
    private static final String TOKEN_NOT_RECOGNISED = "Token was not recognised";
    private static final String ACCESS_DENIED_EXCEPTION = "AccessDeniedException";
    private static final String ACCOUNT_LOCKED = "account is locked";
    private static final String DUPLICATE_ENTRY = "Duplicate entry";
    private static final String FOR_KEY = "for key";


    /**
     * 处理所有不可知的异常
     *
     * @param e ${@link Exception}
     * @return ActionResult ${@link ActionResult}
     */
    @ExceptionHandler(Exception.class)
    public ActionResult<Object> handleException(Exception e) {
        String str = ThrowableUtil.getStackTrace(e);
        log.error("----------处理所有不可知的异常---------->{}", ThrowableUtil.getStackTrace(e));
        if (StringUtils.isNotBlank(str)) {
            if (str.contains(ACCESS_DENIED_EXCEPTION)) {
                return error(HttpStatus.UNAUTHORIZED.value(), e.getMessage());
            } else if (str.contains(TOKEN_EXPIRED)) {
                return error(ResultCode.API_TOKEN_TIMEOUT.getValue(), "token过期");
            } else if (str.contains(TOKEN_NOT_RECOGNISED)) {
                return error(ResultCode.API_TOKEN_TIMEOUT.getValue(), "token无效");
            }
        }
        ActionResult<Object> actionResult = new ActionResult<>();
        actionResult.setSuccess(false);
        actionResult.setCode(HttpStatus.BAD_REQUEST.value());
        actionResult.setMessage("服务器出问题了,请稍后再试...");
        return actionResult;
    }


    /**
     * 处理自定义ActionResult异常
     *
     * @param e ${@link ActionException}
     * @return ActionResult ${@link ActionResult}
     */
    @ExceptionHandler(value = ActionException.class)
    public ActionResult badRequestException(ActionException e) {
        // 打印堆栈信息
        log.error("----------处理自定义ActionResult异常---------->{}", e.getMessage());
        e.printStackTrace();
        Object obj = e.getResult();
        if (obj instanceof ActionResult) {
            ActionResult result = (ActionResult) e.getResult();
            String message = result.getMessage();
            if (StringUtils.isNotBlank(message) && message.contains(ACCOUNT_LOCKED)) {
                result.setMessage("账号已锁定或未激活,请联系管理员");
                result.setSuccess(false);
                result.setCode(ResultCode.AUTH_ACCOUNT_LOCKED.getValue());
            }
            return result;
        } else {
            return error(HttpStatus.BAD_REQUEST.value(), e.getMessage());
        }
    }


    /**
     * 处理Security鉴权异常
     *
     * @param e ${@link AccessDeniedException}
     * @return ActionResult ${@link ActionResult}
     */
    @ExceptionHandler(value = AccessDeniedException.class)
    public ActionResult<Object> accessDeniedException(AccessDeniedException e) {
        // 打印堆栈信息
        log.error("----------处理处理Security鉴权异常---------->{}", e.getMessage());
        return error(HttpStatus.UNAUTHORIZED.value(), e.getMessage());
    }


    /**
     * 处理请求参数校验(实体对象传参)异常
     *
     * @param e ${@link MethodArgumentNotValidException}
     * @return ActionResult ${@link ActionResult}
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ActionResult<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        StringBuilder sb = new StringBuilder();
        List<ObjectError> errorList = e.getBindingResult().getAllErrors();
        for (ObjectError error : errorList) {
            sb.append(",").append(error.getDefaultMessage());
        }
        String errMeg = sb.toString().replaceFirst(",", "");
        // 打印堆栈信息
        log.error("----------处理请求参数校验(实体对象传参)异常---------->:{}", errMeg);
        return error(ResultCode.VERIFICATION_FAILED.getValue(), errMeg);
    }


    /**
     * 处理请求参数校验(普通传参)异常
     *
     * @param e ${@link ConstraintViolationException}
     * @return String> ${@link String>}
     * @author zhouxuanhong
     * @date 2019-06-18 09:35
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.OK)
    ActionResult<Object> handleConstraintViolationException(ConstraintViolationException e) {
        StringBuilder sb = new StringBuilder();
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        for (ConstraintViolation<?> violation : violations) {
            sb.append(",").append(violation.getMessage());
        }
        String errMeg = sb.toString().replaceFirst(",", "");
        log.error("----------处理请求参数校验(普通传参)异常---------->:{}", errMeg);
        return error(ResultCode.VERIFICATION_FAILED.getValue(), errMeg);
    }


    /**
     * 处理所feign异常
     *
     * @param e ${@link FeignException}
     * @return ActionResult ${@link ActionResult}
     */
    @ExceptionHandler(FeignException.class)
    public ActionResult<String> handleFeignException(FeignException e) {
        // 打印堆栈信息
        log.error("----------处理所有feign接口接口调用异常---------->{}", e.getMessage());
        // 清空有关feign的缓存
        InMemoryCache.clearAllLikeStr("feign");
        ActionResult<String> actionResult = new ActionResult<>();
        actionResult.setSuccess(false);
        actionResult.setMessage("调用第三方系统Api失败:" + e.getMessage());
        actionResult.setCode(ResultCode.API_ERROR.getValue());
        return actionResult;
    }


    /**
     * 处理所数据库插入唯一性校验失败异常
     *
     * @param e ${@link DuplicateKeyException}
     * @return ActionResult ${@link ActionResult}
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public ActionResult<String> handleDuplicateKeyException(DuplicateKeyException e) {
        log.error("----------处理所数据库插入唯一性校验失败异常---------->:{}", e.getMessage());
        String message = e.getMessage();
        ActionResult<String> actionResult = new ActionResult<>();
        actionResult.setCode(HttpStatus.BAD_REQUEST.value());
        actionResult.setSuccess(false);
        actionResult.setCode(ResultCode.DATABASE_BASE_ERROR.getValue());
        actionResult.setMessage("关键数据重复，不能再次提交");
        return actionResult;
    }


    /**
     * 处理mybatis操作异常
     *
     * @param e ${@link FeignException}
     * @return ActionResult ${@link ActionResult}
     */
    @ExceptionHandler(MyBatisSystemException.class)
    public ActionResult<String> handleMyBatisSystemException(MyBatisSystemException e) {
        log.error("----------处理mybatis操作异常------111---->:{}", ThrowableUtil.getStackTrace(e));
        String message;
        Throwable cause = e.getCause();
        if (cause instanceof TooManyResultsException) {
            TooManyResultsException tooManyResultsException = (TooManyResultsException) cause;
            message = ResultCode.DATABASE_BASE_ERROR.getDescription() + ",查询结果与接收结果数量不一致:" + tooManyResultsException.getMessage();
        } else if (cause instanceof PersistenceException) {
            PersistenceException persistenceException = (PersistenceException) cause;
            Throwable persistenceExceptionCause = persistenceException.getCause();
            message = persistenceExceptionCause.getMessage();
        } else {
            message = ResultCode.DATABASE_BASE_ERROR.getDescription() + ":" + ThrowableUtil.getStackTrace(e);
        }
        ActionResult<String> actionResult = new ActionResult<>();
        actionResult.setSuccess(false);
        actionResult.setMessage(message);
        actionResult.setCode(ResultCode.DATABASE_BASE_ERROR.getValue());
        return actionResult;
    }


    /**
     * 处理接收参数类型转换异常
     *
     * @param e ${@link FeignException}
     * @return ActionResult ${@link ActionResult}
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ActionResult<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error("----------处理接收参数类型转换异常---------->:{}", ThrowableUtil.getStackTrace(e));
        ActionResult<String> actionResult = new ActionResult<>();
        actionResult.setSuccess(false);
        actionResult.setMessage(ThrowableUtil.getStackTrace(e));
        actionResult.setCode(ResultCode.DATABASE_BASE_ERROR.getValue());
        return actionResult;
    }
}
